package com.wolfeyes.framework.aspect;

import com.wolfeyes.common.utils.network.http.HttpContextUtils;
import com.wolfeyes.common.utils.network.ip.IpUtils;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
//import sun.net.util.IPAddressUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @title web日志跟踪记录（AOP切面类）
 * @projectName film
 * @className WebLogAspect.java
 * @author yanyljava
 * @email yanljava@qq.com
 * @date 2018-07-04 15:53:58
 * @version 1.0.0
 */
@Aspect
//此处注解不能少
@Component
public class WebLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

    /**
     * 参考常见问题FAQ文档：配置多个aop:pointcut
     * @description AOP事务切入点
     * @methodName doWebLogPointCut()
     * @since 2017年07月09日下午5:37:39
     * @createTime 2018年07月09日 下午17:04:13
     */
    @Pointcut("execution( * com.wolfeyes.web.controller..*.*(..))")
    public void doWebLogPointCut() {
    	
    	// 开始切入
    	logger.info("Spring Framework AOP 事务aspectjweaver-V1.8.13开始切入#WebLogAspect");
    }


    @Before("doWebLogPointCut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {

        // 快速获取当前请求对象request
    	HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
    	
        // 记录下请求内容
        logger.info("请求地址 : " + request.getRequestURL().toString());
        
        logger.info("HTTP METHOD : " + request.getMethod());
        
        //获取客户端IP地址，如果通过代理进来，则透过防火墙获取真实IP地址
        String ip = IpUtils.getIpAddr(request);
        
        // 获取请求主机真实的ip地址
        logger.info("客户端IP : " + ip);
        
    	String methodInfo = joinPoint.getSignature().toString();
    	//String methodInfo = methodSignature.toString();
    	int methodModifiers = joinPoint.getSignature().getModifiers();
    	// print out the modifiers
    	String methodModifiersFull = java.lang.reflect.Modifier.toString(methodModifiers);
    	String methodInfoFull = "[[<< " + methodModifiersFull + " " + methodInfo + " >>]]";
    	//Object[] args = proceedingJoinPoint.getArgs();
    	
    	logger.info("[step2]即将执行-->>CLASS_METHOD : " + methodInfoFull);
    	
        //logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
        //        + joinPoint.getSignature().getName());
        
    	Object[] args = joinPoint.getArgs();
        logger.info("参数 : " + Arrays.toString(args));
//        loggger.info("参数 : " + joinPoint.getArgs());
        
		Object targetObject = joinPoint.getTarget();
		//Object[] args = joinPoint.getArgs();
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
		Method method = methodSignature.getMethod();
		
		/*
		int length = args.length;
		Class<?>[] argsClazz = new Class[length];
		for(int index = 0; index < length; index++) {
			if(args[index].getClass().getName().contains("Map")) {
				argsClazz[index] = args[index].getClass().getSuperclass().getInterfaces()[0];
			} else {
				argsClazz[index] = args[index].getClass();
			}
		}
		Class<? extends Object> controllerClazz = joinPoint.getSignature().getDeclaringType();
		String className = joinPoint.getSignature().getDeclaringTypeName();
		String methodName = joinPoint.getSignature().getName();
		Method method = controllerClazz.getDeclaredMethod(methodName, argsClazz);
		
		SysExceptInfo sysExceptInfo = method.getAnnotation(SysExceptInfo.class);
		*/

    }

    @AfterReturning(pointcut = "doWebLogPointCut()", returning = "result")// returning的值和doAfterReturning的参数名一致
    public void doAfterReturning(JoinPoint joinPoint, Object result) throws Throwable {
    	
        // 处理完请求，返回内容(返回值太复杂时，打印的是物理存储空间的地址)
        logger.debug("[step3]返回值 : " + result);
    }

    @After("doWebLogPointCut()")
    public void doAfter(JoinPoint joinPoint) throws Throwable {
    	
    	String methodInfo = joinPoint.getSignature().toString();
    	//String methodInfo = methodSignature.toString();
    	int methodModifiers = joinPoint.getSignature().getModifiers();
    	// print out the modifiers
    	String methodModifiersFull = java.lang.reflect.Modifier.toString(methodModifiers);
    	String methodInfoFull = "[[<< " + methodModifiersFull + " " + methodInfo + " >>]]";
    	//Object[] args = proceedingJoinPoint.getArgs();
    	
    	logger.debug("[step4]执行完毕-->>CLASS_METHOD : " + methodInfoFull);
    	
        //logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
        //        + joinPoint.getSignature().getName());
        
        //logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
//        loggger.info("参数 : " + joinPoint.getArgs());
    }
    
    /**
     * @title 环绕通知-AOP
     * @description 环绕通知是集大成者，可以用环绕通知实现上面的四个通知，这个方法的核心有点类似于在这里通过反射执行方法
     * @param proceedingJoinPoint 执行切点
     * @author yanyl
     * @return 注意这里的返回值类型最好是 Object ，和拦截到的方法相匹配
     */
    @Around("doWebLogPointCut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        
    	System.out.println("WebLogAspect aspect start");
    	Object[] args = proceedingJoinPoint.getArgs();
    	logger.info("参数 : " + Arrays.toString(args));
    	/*
    	List<Object> args = Arrays.asList(args);
    	for (Object arg : args) {
    		System.out.println("arg is : " + arg);
    		
    	}
    	*/
    	
    	Object targetObject = proceedingJoinPoint.getTarget();
    	// 获取签名
    	MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
    	// 获取切入的包（路径）名
    	String className = proceedingJoinPoint.getSignature().getDeclaringTypeName();
    	//String className = methodSignature.getDeclaringTypeName();
    	String methodName = proceedingJoinPoint.getSignature().getName();
    	//String methodName = methodSignature.getName();
    	Method method = methodSignature.getMethod();
    	String class_method = className + "." + methodName;
    	String methodInfo = proceedingJoinPoint.getSignature().toString();
    	//String methodInfo = methodSignature.toString();
    	int methodModifiers = proceedingJoinPoint.getSignature().getModifiers();
    	// print out the modifiers
    	String methodModifiersFull = java.lang.reflect.Modifier.toString(methodModifiers);
    	String methodInfoFull = "[[<< " + methodModifiersFull + " " + methodInfo + " >>]]";
    	//Object[] args = proceedingJoinPoint.getArgs();
    	
        //logger.info("开始执行-->>CLASS_METHOD : " + class_method);
    	logger.info("[step1]开始执行-->>CLASS_METHOD : " + methodInfoFull);
        
        long startTime = System.currentTimeMillis();
        
        // object 为方法的返回值
        // 这个相当于 method.invoke 方法，我们可以在这个方法的前后分别添加日志，就相当于是前置/后置通知
        Object object = proceedingJoinPoint.proceed();
        
        long endTime = System.currentTimeMillis();
        
        //logger.info("成功执行-->>CLASS_METHOD : " + class_method);
        logger.info("[step5]成功执行-->>CLASS_METHOD : " + methodInfoFull);
        
		// 耗时：单位（秒）
		//System.out.println("用时 : " + (endTime - startTime) / 1000d + "秒");
        //logger.info("耗时 : " + (endTime - startTime) + " ms");
        logger.info("用时 : " + (endTime - startTime) / 1000d + "秒");
        
        System.out.println("WebLogAspect aspect end");
        
        return object;
    }
    
    /**
     * @title 被切方法执行过程中抛出异常的处理逻辑
     */
    @AfterThrowing(pointcut = "doWebLogPointCut()", throwing = "ex")// throwing的值和doAfterThrowing的参数名一致
    private void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {
    	//Signature signature = joinPoint.getSignature();
    	//String method = signature.getName();
    	String methodInfo = joinPoint.getSignature().toString();
    	//String methodInfo = methodSignature.toString();
    	int methodModifiers = joinPoint.getSignature().getModifiers();
    	// print out the modifiers
    	String methodModifiersFull = java.lang.reflect.Modifier.toString(methodModifiers);
    	String methodInfoFull = "[[<< " + methodModifiersFull + " " + methodInfo + " >>]]";
    	//Object[] args = proceedingJoinPoint.getArgs();
    	
    	logger.debug("[step6]执行异常-->>CLASS_METHOD : " + methodInfoFull);
    	// 处理异常的逻辑
    	logger.info("执行方法{}出错，异常为：{}", methodInfoFull, ex);

	}
    
}
